package security;

import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import org.apache.commons.codec.binary.Base64;

/**
 *
 * @author Tom
 */
public class RSAKeyPair
{
    static final Base64 encoder = new Base64();
    Key publicKey;
    Key privateKey;
    
    public RSAKeyPair(byte[] keyEncoded, boolean isPublic)
    {        
        KeyFactory fact;
        if (isPublic)
        {
            try 
            {
                fact = KeyFactory.getInstance("RSA");
                X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyEncoded);
                publicKey = fact.generatePublic(keySpec);
                privateKey=null;
            }
            catch (NoSuchAlgorithmException | InvalidKeySpecException ex) 
            {
                publicKey=null;
                privateKey=null;
                ex.printStackTrace();
            }
        }
        else
        {
            try 
            {
                fact = KeyFactory.getInstance("RSA");
                PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyEncoded);
                privateKey = fact.generatePublic(keySpec);      
                publicKey=null;
            }
            catch (NoSuchAlgorithmException | InvalidKeySpecException ex) 
            {
                publicKey=null;
                privateKey=null;
                ex.printStackTrace();
            }
        }
    }
    
    public RSAKeyPair(byte[] publicKeyEncoded, byte[] privateKeyEncoded)
    {     
        KeyFactory fact;
        try 
        {
            fact = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec pubkeySpec = new X509EncodedKeySpec(publicKeyEncoded);
            publicKey = fact.generatePublic(pubkeySpec);

            PKCS8EncodedKeySpec prikeySpec = new PKCS8EncodedKeySpec(privateKeyEncoded);
            privateKey = fact.generatePrivate(prikeySpec);
        } 
        catch (NoSuchAlgorithmException ex) 
        {
            //never happen
            ex.printStackTrace();
        } 
        catch (InvalidKeySpecException | NullPointerException ex)
        {
            System.out.println("null pointer??? or invalid??");
            publicKey=null;
            privateKey=null;
        }
    }
    
    public RSAKeyPair(String random)
    {
        try
        {
            KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
            kpg.initialize(1024, new SecureRandom(random.getBytes()));
            KeyPair kp = kpg.genKeyPair();
            publicKey = kp.getPublic();
            privateKey = kp.getPrivate();
        } 
        catch (NoSuchAlgorithmException ex) 
        {
            ex.printStackTrace();
        }
    }
    
    public String getPublicKeyBase64()
    {       
        return encoder.encodeToString(publicKey.getEncoded());
    }
    
    public byte[] getPrivateKeyEncoded()
    {        
        return privateKey.getEncoded();
    }
    
    public byte[] encodeByPrivateKey(byte[] data)
    {
        return encode(data, privateKey);
    }
    
    public byte[] encodeByPublicKey(byte[] data)
    {
        return encode(data, publicKey);
    }
    
    public byte[] decodeByPrivateKey(byte[] data)
    {
        return decode(data,privateKey);
    }
    
    public byte[] decodeByPublicKey(byte[] data)
    {
        return decode(data, publicKey);
    }
    
    private byte[] decode(byte[] data, Key key)
    {
        try 
           {
               Cipher cipher = Cipher.getInstance("RSA");
               cipher.init(Cipher.DECRYPT_MODE, key);            
               return cipher.doFinal(data);            
           } 
           catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | 
                   IllegalBlockSizeException | BadPaddingException ex) {            
               ex.printStackTrace();
           }
           return null;   
    }
    
    
    private byte[] encode(byte[] data, Key key)
    {
        try 
        {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, key);            
            return cipher.doFinal(data);            
        } 
        catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | 
                IllegalBlockSizeException | BadPaddingException ex) {
            ex.printStackTrace();
        }
        return null;
    }
}